//  -----------------------------------------------------------------------------------------------//
//   This file is part of SvnExternalUpdater                                                               //
//   Copyright (C)2008 Romain Verdier ( romain@codingly.com )                                      //
//                                                                                                 //
//   This program is free software; you can redistribute it and/or modify it under the terms       //
//   of the GNU General Public License as published by the Free Software Foundation; either        //
//   version 2 of the License, or (at your option) any later version. This program is distributed  //
//   in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied        //
//   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public  //
//   License for more details.                                                                     //
//                                                                                                 //
//   You should have received a copy of the GNU General Public License along with this program; if //
//   not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. or  //
//   see http://www.fsf.org/licensing/licenses/gpl.html                                            //
//  -----------------------------------------------------------------------------------------------//
using System;
using System.Collections.Generic;
using Codingly.SvnExternalUpdater.Core.Model;

namespace Codingly.SvnExternalUpdater.Core
{
    internal static class ExternalFactory
    {
        internal static External Create(string externalDefinition, IProject parent, Uri repositoryRootUrl, IRemoteFolderProvider remoteFolderProvider)
        {
            ExternalDefinitionParser parser = FindMachingParser(externalDefinition);
            External external = null;
            if (parser != null)
            {
                string url = parser.Url;
                Uri absoluteUrl = ResolveAbsoluteUrl(url, parent.Url, repositoryRootUrl);

                IRemoteFolder remoteFolder = remoteFolderProvider.GetOrCreateRemoteFolder(absoluteUrl);
                external = new External(remoteFolder, parent)
                               {
                                   HasRevision = parser.HasRevision, 
                                   Path = parser.FolderName, 
                                   LocalRevision = parser.Revision, 
                                   Type = parser.Type,
                                   RelativeUrl = parser.Url
                               };
            }
            return external;
        }

        private static ExternalDefinitionParser FindMachingParser(string external)
        {
            ExternalDefinitionParser result = null;
            foreach (ExternalDefinitionParser parser in GetExternalDefinitionParsers())
            {
                if (parser.Match(external))
                {
                    result = parser;
                    break;
                }
            }
            return result;
        }

        private static IEnumerable<ExternalDefinitionParser> GetExternalDefinitionParsers()
        {
            var possibleTypes = new[] {ExternalType.RevisionFirst, ExternalType.UrlFirstWithRevision, ExternalType.UrlFirst, ExternalType.FolderFirstWithoutRevision, ExternalType.FolderFirst};

            foreach (ExternalType possibleType in possibleTypes)
            {
                yield return new ExternalDefinitionParser(possibleType);
            }
        }

        private static Uri ResolveAbsoluteUrl(string url, Uri parentUrl, Uri repositoryRootUrl)
        {
            //  "../"   = Relative to the URL of the directory on which the svn:externals property is set
            if (url.StartsWith("../"))
            {
                return ResolveFromBaseUrl(url, parentUrl);
            }

            //  "^/"    = Relative to the root of the repository in which the svn:externals property is versioned
            if (url.StartsWith("^/"))
            {
                url = url.Remove(0, 2);
                return ResolveFromBaseUrl(url, repositoryRootUrl);
            }

            //  "//"    = Relative to the scheme of the URL of the directory on which the svn:externals property is set
            if (url.StartsWith("//"))
            {
                return ResolveFromUrlScheme(url, parentUrl);
            }

            //  "/"     = Relative to the root URL of the server on which the svn:externals property is versioned
            if (url.StartsWith("/"))
            {
                return ResolveFromServerRootUrl(url, parentUrl);
            }

            return new Uri(url);
        }

        private static Uri ResolveFromBaseUrl(string url, Uri baseUrl)
        {
            Uri absoluteUrl;
            if (!Uri.TryCreate(baseUrl, url, out absoluteUrl))
            {
                absoluteUrl = null;
            }
            return absoluteUrl;
        }

        private static Uri ResolveFromServerRootUrl(string url, Uri parentUrl)
        {
            var builder = new UriBuilder(parentUrl.Scheme, parentUrl.Host);
            return ResolveFromBaseUrl(url, builder.Uri);
        }

        private static Uri ResolveFromUrlScheme(string url, Uri parentUrl)
        {
            var builder = new UriBuilder(parentUrl.Scheme, url);
            return builder.Uri;
        }
    }
}